#include "src/utils/utils.h"
#include "src/resizepad_cvtcolor_nhwc2nchw_normlize/resizepad_cvtcolor_nhwc2nchw_normlize.h"


int check_cuda_opencv_result(float* A,float* B,tools::Param param){
    int ans=0;
    for (int c = 0; c < 3; ++c) {
        for (int h = 0; h < param.dst_h; ++h) {
            for (int w = 0; w <param.dst_w; ++w) {
                int index = c * param.dst_h * param.dst_w + h * param.dst_w + w;
                
                if(fabs(A[index]-B[index])>0.1){
                    // std::cout << "Blob[" << c << "][" << h << "][" << w << "]: " << "cuda:"<<A[index]<<"  cpu: "<<B[index] << " diff:"<<fabs(A[index]-B[index])<<std::endl;
                    ans += 1;
                }
                }
            }
        }
    return ans;
    }


int main(){
    tools::Param  param;
    tools::AffineMatrix matrix_src2dst;
    tools::AffineMatrix matrix_dst2src;
    cv::Mat src2dstmat;
    cv::Mat dst2srcmat;
    cv::Mat img = cv::imread("../a.jpg");
    param.src_h = img.rows;
    param.src_w = img.cols;
    float scale = std::min(param.dst_h/(float)param.src_h, param.dst_w/float(param.src_w));
    float offset_x = 0.5*(param.dst_w-param.src_w*scale+scale-1);
    float offset_y = 0.5*(param.dst_h-param.src_h*scale+scale-1);
    src2dstmat = (cv::Mat_<float>(2,3,CV_32FC1)<<scale,0,offset_x,0,scale,offset_y);
    cv::invertAffineTransform(src2dstmat,dst2srcmat);
    matrix_dst2src.v0 = dst2srcmat.ptr<float>(0)[0];
    matrix_dst2src.v1 = dst2srcmat.ptr<float>(0)[1];
    matrix_dst2src.v2 = dst2srcmat.ptr<float>(0)[2];
    matrix_dst2src.v3 = dst2srcmat.ptr<float>(1)[0];
    matrix_dst2src.v4 = dst2srcmat.ptr<float>(1)[1];
    matrix_dst2src.v5 = dst2srcmat.ptr<float>(1)[2];
    
    unsigned char* batch_imgs;
    float* batch_imgs_prepocessed_host;
    float* batch_imgs_prepocessed_device;
    // 将多个cv::Mat host指针数据形成dynamicbatch
    cudaMalloc((void**)&batch_imgs,param.batch*param.src_w*param.src_h*3*sizeof(unsigned char));
    // 为cuda核函数计算结果在host端展示的空间分配内存
    batch_imgs_prepocessed_host = (float*)malloc(param.batch*param.dst_h*param.dst_w*3*sizeof(float));
    // 为cuda核函数计算结果在device端的空间分配内存
    cudaMalloc((void**)&batch_imgs_prepocessed_device,param.batch*param.dst_h*param.dst_w*3*sizeof(float));
    // 将cv::Mat host指针数据拷贝到device
    for(int i=0;i<param.batch;++i){
        cudaMemcpy(batch_imgs+i*param.src_w*param.src_h*3,img.data,
        param.src_w*param.src_h*3*sizeof(unsigned char),cudaMemcpyHostToDevice);
    }
    for(int i=0;i<param.epochs;++i){
        DeviceTimer dt0; 
        launch_cuda_resize_padding_nhwc2nchw_normlize(param,batch_imgs,batch_imgs_prepocessed_device,matrix_dst2src);
        float dc0 = dt0.getUsedTime()/param.batch;
        printf("epoch %d cuda op const %f ,img_width: %d img_height %d\n",i,dc0,img.cols,img.rows);
    }
    printf("cpu propcess!!\n");
    cv::Mat img_cpu;
    cv::Mat img_cpu_gray(cv::Size(param.dst_w,param.dst_h),img.type(),cv::Scalar(127,127,127));
    int target_w = (int)param.src_w*scale;
    int target_h = (int)param.src_h*scale;
    // offset_x = (param.dst_h-target_h)/2;
    // offset_y = (param.dst_w-target_w)/2;
    offset_x = std::max(0, (param.dst_w - target_w) / 2);
    offset_y = std::max(0, (param.dst_h - target_h) / 2);

    cv::Mat blob;
    for(int j=0;j<param.epochs;++j){
        HostTimer ht0; 
        cv::resize(img,img_cpu,cv::Size(target_w,target_h),0,0,cv::INTER_LINEAR);
        // cv::cvtColor(img_cpu,img_cpu,cv::COLOR_BGR2RGB);
        img_cpu.copyTo(img_cpu_gray(cv::Rect(offset_x,offset_y,img_cpu.cols,img_cpu.rows)));
        cv::dnn::blobFromImage(img_cpu_gray, blob, 1.0/255.0, cv::Size {640, 640}, cv::Scalar(), true, false);
        float hc1 = ht0.getUsedTime();
        printf("epoch %d cpu opencv const %f ,img_width: %d img_height %d\n",j,hc1,img.cols,img.rows);

    }
    // cv::imwrite("cpu_rezizepadbgr.jpg",img_cpu_gray);
    
    cudaMemcpy(batch_imgs_prepocessed_host,batch_imgs_prepocessed_device,
        param.batch*param.dst_h*param.dst_w*3*sizeof(float),cudaMemcpyDeviceToHost);

    if (blob.empty()) {
        std::cerr << "Error: blob is empty!" << std::endl;
        return -1;
    }
        
    std::cout << "Blob shape: " << blob.size << std::endl;
    float* blob_data = (float*)blob.data;

    // 遍历前三个像素点
    

    int success = check_cuda_opencv_result(batch_imgs_prepocessed_host,blob_data,param);
    if(!success){
        printf("cuda && opencv get the same result!\n");
    }else{
        printf("there are %d elements diff!\n",success);    
    }

    free(batch_imgs_prepocessed_host);
    cudaFree(batch_imgs);
    cudaFree(batch_imgs_prepocessed_host);
    
}





